package cn.hg.solon.youcan.framework.directive;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;

import org.dromara.hutool.core.text.StrValidator;
import org.noear.solon.Solon;

import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.Scope;

/**
 * @author 胡高
 */
@SuppressWarnings("unchecked")
public abstract class BaseDirective extends Directive {

    public BaseDirective() {
        Solon.context().beanInject(this);
    }

    @Override
    public void exec(Env env, Scope scope, Writer writer) {
        scope = new Scope(scope);
        scope.getCtrl().setLocalAssignment();
        this.exprList.eval(scope);
        this.onRender(env, scope, writer);
    }


    public <T> T getPara(int index, Scope scope) {
        return this.getPara(index, scope, null);
    }


    public <T> T getPara(int index, Scope scope, T defaultValue) {
        if (index < 0 || index >= this.exprList.length()) {
            return defaultValue;
        }
        Object data = this.exprList.getExpr(index).eval(scope);
        return (T) (data == null ? defaultValue : data);
    }


    public <T> T getPara(String key, Scope scope) {
        return this.getPara(key, scope, null);
    }

    public <T> T getPara(String key, Scope scope, T defaultValue) {
        Object data = scope.getLocal(key);
        return (T) (data == null ? defaultValue : data);
    }

    public Map<Object, Object> getParas(Scope scope) {
        return scope.getData();
    }

    public BigDecimal getParaToBigDecimal(int index, Scope scope) {
        Object object = this.getPara(index, scope, null);
        if (object == null || object instanceof BigDecimal) {
            return (BigDecimal) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : new BigDecimal(objStr);
    }

    public BigDecimal getParaToBigDecimal(int index, Scope scope, BigDecimal defaultValue) {
        BigDecimal v = this.getParaToBigDecimal(index, scope);
        return v == null ? defaultValue : v;
    }

    public BigDecimal getParaToBigDecimal(String key, Scope scope) {
        Object object = this.getPara(key, scope, null);
        if (object == null || object instanceof BigDecimal) {
            return (BigDecimal) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : new BigDecimal(objStr);
    }

    public BigDecimal getParaToBigDecimal(String key, Scope scope, BigDecimal defaultValue) {
        BigDecimal v = this.getParaToBigDecimal(key, scope);
        return v == null ? defaultValue : v;
    }


    public BigInteger getParaToBigInteger(int index, Scope scope) {
        Object object = this.getPara(index, scope, null);
        if (object == null || object instanceof BigInteger) {
            return (BigInteger) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : new BigInteger(objStr);
    }


    public BigInteger getParaToBigInteger(int index, Scope scope, BigInteger defaultValue) {
        BigInteger v = this.getParaToBigInteger(index, scope);
        return v == null ? defaultValue : v;
    }


    public BigInteger getParaToBigInteger(String key, Scope scope) {
        Object object = this.getPara(key, scope, null);
        if (object == null || object instanceof BigInteger) {
            return (BigInteger) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : new BigInteger(objStr);
    }


    public BigInteger getParaToBigInteger(String key, Scope scope, BigInteger defaultValue) {
        BigInteger v = this.getParaToBigInteger(key, scope);
        return v == null ? defaultValue : v;
    }

    public Boolean getParaToBool(int index, Scope scope) {
        Object object = this.getPara(index, scope, null);
        if (object == null || object instanceof Boolean) {
            return (Boolean) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : Boolean.valueOf(objStr);
    }

    public Boolean getParaToBool(int index, Scope scope, Boolean defaultValue) {
        Boolean v = this.getParaToBool(index, scope);
        return v == null ? defaultValue : v;
    }

    public Boolean getParaToBool(String key, Scope scope) {
        Object object = this.getPara(key, scope, null);
        if (object == null || object instanceof Boolean) {
            return (Boolean) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : Boolean.valueOf(objStr);
    }


    public Boolean getParaToBool(String key, Scope scope, Boolean defaultValue) {
        Boolean v = this.getParaToBool(key, scope);
        return v == null ? defaultValue : v;
    }

    public Integer getParaToInt(int index, Scope scope) {
        Object object = this.getPara(index, scope, null);
        if (object == null || object instanceof Integer) {
            return (Integer) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : Integer.valueOf(objStr);
    }

    public Integer getParaToInt(int index, Scope scope, Integer defaultValue) {
        Integer v = this.getParaToInt(index, scope);
        return v == null ? defaultValue : v;
    }

    public Integer getParaToInt(String key, Scope scope) {
        Object object = this.getPara(key, scope, null);
        if (object == null || object instanceof Integer) {
            return (Integer) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : Integer.valueOf(objStr);
    }

    public Integer getParaToInt(String key, Scope scope, Integer defaultValue) {
        Integer v = this.getParaToInt(key, scope);
        return v == null ? defaultValue : v;
    }

    public Long getParaToLong(int index, Scope scope) {
        Object object = this.getPara(index, scope, null);
        if (object == null || object instanceof Long) {
            return (Long) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : Long.valueOf(objStr);
    }

    public Long getParaToLong(int index, Scope scope, Long defaultValue) {
        Long v = this.getParaToLong(index, scope);
        return v == null ? defaultValue : v;
    }

    public Long getParaToLong(String key, Scope scope) {
        Object object = this.getPara(key, scope, null);
        if (object == null || object instanceof Long) {
            return (Long) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : Long.valueOf(objStr);
    }

    public Long getParaToLong(String key, Scope scope, Long defaultValue) {
        Long v = this.getParaToLong(key, scope);
        return v == null ? defaultValue : v;
    }

    public String getParaToString(int index, Scope scope) {
        Object object = this.getPara(index, scope, null);
        if (object == null || object instanceof String) {
            return (String) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : objStr;
    }

    public String getParaToString(int index, Scope scope, String defaultValue) {
        String v = this.getParaToString(index, scope);
        return v == null ? defaultValue : v;
    }

    public String getParaToString(String key, Scope scope) {
        Object object = this.getPara(key, scope, null);
        if (object == null || object instanceof String) {
            return (String) object;
        }
        String objStr = object.toString();
        return StrValidator.isBlank(objStr) ? null : objStr;
    }


    public String getParaToString(String key, Scope scope, String defaultValue) {
        String v = this.getParaToString(key, scope);
        return v == null ? defaultValue : v;
    }

    public Map<Object, Object> getRootParas(Scope scope) {
        return scope.getRootData();
    }

    public abstract void onRender(Env env, Scope scope, Writer writer);


    public void renderBody(Env env, Scope scope, Writer writer) {
        this.stat.exec(env, scope, writer);
    }


    public void renderText(Writer writer, String text) {
        try {
            writer.write(text);
        } catch (IOException e) {
            throw new TemplateException(e.getMessage(), this.location, e);
        }
    }

    @Override
    public void setExprList(ExprList exprList) {
        super.setExprList(exprList);
    }

}
